package com.ly.im.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class WebSocketServer implements IMNettyServer{

    private final Logger logger = LoggerFactory.getLogger(WebSocketServer.class);

    private volatile boolean ready = false;

    private EventLoopGroup bossGroup;
    private EventLoopGroup workGroup;

    public int port = 8004;

    @Override
    public boolean isReady() {
        return ready;
    }

    @Override
    public void start(){
        ServerBootstrap bootstrap = new ServerBootstrap();
        bossGroup = new NioEventLoopGroup();
        workGroup = new NioEventLoopGroup();
            bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
            bootstrap.group(bossGroup, workGroup) // 绑定线程池
                    .channel(NioServerSocketChannel.class) // 指定使用的channel
                    .childHandler(new ChannelInitializer<SocketChannel>() { // 绑定客户端连接时候触发操作
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            // 获取职责链
                            ChannelPipeline pipeline = ch.pipeline();
                            // 心跳检测(一般情况第一个设置,如果超时了,则会调用userEventTriggered方法,且会告诉你超时的类型)
                            pipeline.addLast(new IdleStateHandler(120, 0, 0, TimeUnit.SECONDS));
                            // HTTP协议的编解码器，webSocket协议本身是基于http协议的，所以这边也要使用http编解码器
                            pipeline.addLast("http-codec", new HttpServerCodec());
                            /*
                                说明：
                                1、http数据在传输过程中是分段的，HttpObjectAggregator可以将多个段聚合(用于将多个HTTP消息聚合成一个完整的消息，以便在单个Channel上传输)
                                2、这就是为什么，当浏览器发送大量数据时，就会发送多次http请求
                             */
                            pipeline.addLast("aggregator", new HttpObjectAggregator(65535));
                            // 将消息分块发送的编解码器
                            pipeline.addLast("http-chunked", new ChunkedWriteHandler());
                            /*
                                说明：
                                1、对应webSocket，它的数据是以帧（frame）的形式传递
                                2、浏览器请求时 ws://localhost:58080/xxx 表示请求的uri
                                3、核心功能是将http协议升级为ws协议，保持长连接
                            */
                            pipeline.addLast(new WebSocketServerProtocolHandler("/ws", "WebSocket", true, 65536 * 10));
                            // 自定义的handler，处理业务逻辑
                            pipeline.addLast("handler",  new WebSocketHandler());
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);
        try {
            bootstrap.bind(port).sync().channel();
            this.ready = true;
            logger.info("WebSocketServer.start|websocket server 初始化完成,端口：{}", port);
        } catch (InterruptedException e) {
            logger.info("WebSocketServer.start|websocket server 初始化异常", e);
        }
    }

    @Override
    public void shutdown() {
        if(bossGroup != null && !bossGroup.isShuttingDown() && !bossGroup.isShutdown() ) {
            bossGroup.shutdownGracefully();
        }
        if(workGroup != null && !workGroup.isShuttingDown() && !workGroup.isShutdown() ) {
            workGroup.shutdownGracefully();
        }
        this.ready = false;
        logger.info("WebSocketServer.shutdown|websocket server 停止");
    }
}
